﻿namespace Neovolve.Extensibility.ReSharper
{
    using System;
    using System.Collections.Generic;
    using JetBrains.Application.Progress;
    using JetBrains.DocumentModel;
    using JetBrains.ReSharper.Daemon;
    using JetBrains.ReSharper.Psi;
    using JetBrains.ReSharper.Psi.CSharp.Tree;
    using JetBrains.ReSharper.Psi.Tree;

    /// <summary>
    /// The <see cref="ConvertTypeRecursiveElementProcessor"/>
    ///   class is used to find <see cref="ConvertToClrTypeHighlighting"/> instances in a document.
    /// </summary>
    internal class ConvertTypeRecursiveElementProcessor : IRecursiveElementProcessor
    {
        /// <summary>
        /// Stores the highlightings for the stage.
        /// </summary>
        private readonly List<HighlightingInfo> _highlightings = new List<HighlightingInfo>();

        /// <summary>
        /// Stores the daemon process reference.
        /// </summary>
        private readonly IDaemonProcess _process;

        /// <summary>
        /// Initializes a new instance of the <see cref="ConvertTypeRecursiveElementProcessor"/> class.
        /// </summary>
        /// <param name="process">
        /// The process.
        /// </param>
        public ConvertTypeRecursiveElementProcessor(IDaemonProcess process)
        {
            _process = process;
        }

        /// <summary>
        /// The interior should be processed.
        /// </summary>
        /// <param name="element">
        /// The element.
        /// </param>
        /// <returns>
        /// <c>true</c> if the interior should be processed; otherwise <c>false</c>.
        /// </returns>
        public Boolean InteriorShouldBeProcessed(IElement element)
        {
            return true;
        }

        /// <summary>
        /// The process after interior.
        /// </summary>
        /// <param name="element">
        /// The element.
        /// </param>
        public void ProcessAfterInterior(IElement element)
        {
        }

        /// <summary>
        /// The process before interior.
        /// </summary>
        /// <param name="element">
        /// The element.
        /// </param>
        public void ProcessBeforeInterior(IElement element)
        {
            if (ProcessingIsFinished)
            {
                return;
            }

            NodeDescriptor nodeDescriptor = null;
            IDocCommentNode commentNode = element as IDocCommentNode;

            if (commentNode != null)
            {
                nodeDescriptor = NodeAnalyser.GetNodeDescriptor(commentNode, -1);

                ProcessCommentDescriptor(nodeDescriptor);
            }
            else
            {
                ITokenNode tokenNode = element as ITokenNode;

                if (tokenNode == null)
                {
                    return;
                }

                nodeDescriptor = NodeAnalyser.GetNodeDescriptor(tokenNode);

                ProcessDescriptor(nodeDescriptor);
            }
        }

        /// <summary>
        /// Processes the comment descriptor.
        /// </summary>
        /// <param name="nodeDescriptor">
        /// The node descriptor.
        /// </param>
        private void ProcessCommentDescriptor(NodeDescriptor nodeDescriptor)
        {
            CommentNodeSetDescriptor commentSetDescriptor = nodeDescriptor as CommentNodeSetDescriptor;

            if (commentSetDescriptor == null)
            {
                return;
            }

            foreach (CommentNodeDescriptor commentNodeDescriptor in commentSetDescriptor.Descriptors)
            {
                ProcessDescriptor(commentNodeDescriptor);
            }
        }

        /// <summary>
        /// Processes the descriptor.
        /// </summary>
        /// <param name="nodeDescriptor">
        /// The node descriptor.
        /// </param>
        private void ProcessDescriptor(NodeDescriptor nodeDescriptor)
        {
            if (nodeDescriptor == null)
            {
                return;
            }

            ConvertTypeHighlighting highlighting = null;

            if (nodeDescriptor.IsClrType && ConvertToAliasTypeHighlighting.CurrentSeverity > Severity.DO_NOT_SHOW)
            {
                IConvertTypeBulbItem bulbItem = new ConvertTypeBulbItem(nodeDescriptor);

                highlighting = new ConvertToAliasTypeHighlighting(bulbItem);
            }
            else if (nodeDescriptor.IsClrType == false && ConvertToClrTypeHighlighting.CurrentSeverity > Severity.DO_NOT_SHOW)
            {
                IConvertTypeBulbItem bulbItem = new ConvertTypeBulbItem(nodeDescriptor);

                highlighting = new ConvertToClrTypeHighlighting(bulbItem);
            }

            if (highlighting == null)
            {
                return;
            }

            DocumentRange range = nodeDescriptor.GetDocumentRange();
            HighlightingInfo info = new HighlightingInfo(range, highlighting);

            _highlightings.Add(info);
        }

        /// <summary>
        /// Gets the highlightings.
        /// </summary>
        /// <value>
        /// The highlightings.
        /// </value>
        public List<HighlightingInfo> Highlightings
        {
            get
            {
                return _highlightings;
            }
        }

        /// <summary>
        /// Gets a value indicating whether ProcessingIsFinished.
        /// </summary>
        /// <exception cref="ProcessCancelledException">
        /// </exception>
        /// <value>
        /// The processing is finished.
        /// </value>
        public Boolean ProcessingIsFinished
        {
            get
            {
                if (_process.InterruptFlag)
                {
                    throw new ProcessCancelledException();
                }

                return false;
            }
        }
    }
}